#ifndef P8X32A_H
#define P8X32A_H

#include <QThread>
#include <QDebug>
#include <QHash>
#include <QTime>
#include <QLabel>
#include <QPlainTextEdit>

#include <micro.h>
#include "traceeditor.h"

typedef int int32;
typedef unsigned int uint32;

class CogInfo
{
public:
    uint pc;
    uint ins;
    bool z;
    bool c;
    bool wr;
    uint cond;
    uint src;
    uint dst;
    uint srcval;
    uint dstval;
    uint result;
    uint PAR ;
    uint CNT ;
    uint INA ;
    uint INB ;
    uint OUTA;
    uint OUTB;
    uint DIRA;
    uint DIRB;
    uint CTRA;
    uint CTRB;
    uint FRQA;
    uint FRQB;
    uint PHSA;
    uint PHSB;
    uint VCFG;
    uint VSCL;
    QList<uint> codeList;
};

class P8x32a : public Micro
{
    Q_OBJECT
public:
    explicit P8x32a(Micro *parent = 0);
    virtual ~P8x32a();

    void    boot();
    void    initialize();
    int     load(const char *progptr, int size);
    void    run();
    static  QString getInstName(int opcode, int zcri, int cond, int src);
    static  QString getCondition(int opcond);

    void    clearBreakpoints();
    void    showTrace(bool visible);

    void    setDebugCycleMode(bool enable);
    void    updateState();

private:
    void    copyShowHub(const char *ptr, int size);
    void    resethub();
    void    resetcog(int num = 0);
    int     coginit(uint32 *codep, int par, int num, int newcog);

    uint32  getPins(int cog);
    void    setPins(int cog, uint32 val);
    void    setPinDir(int cog, uint32 val);

    int32   parity(uint32 val);
    int32   abs(int32 val);
    void    AdjustPipeForJump(uint32 value, uint32 jump, int cog);

    int32   msTime();
    int32   getCount();
    int     execute(int cog, bool debug);

    void    highlightCurrentLine(QPlainTextEdit *ed, QColor lineColor);

public:
    enum { COGCOUNT = 8  };
    enum { PINCOUNT = 32 };
    enum { COGSIZE  = 512 };
    enum { COGCODESIZE = 496 };
    enum { MEMSIZE  = 0x10000 };
    enum { RAMSIZE  = 0x8000 };
    enum { ROMSIZE  = 0x8000 };
    enum { RAMSTART = 0x0 };
    enum { ROMSTART = 0x8000 };

    enum { COGINIT_MASK     = 0x800 };
    enum { DELAYSLOT_MASK   = 0x400 };
    enum { JUMPINSTR_MASK   = 0x200 };
    enum { REG_MASK = COGSIZE-1 };

    // special registers
    //
    enum { REG_PAR  = 0x1f0 }; // Read-Only1 Boot Parameter
    enum { REG_CNT  = 0x1f1 }; // Read-Only1 System Counter
    enum { REG_INA  = 0x1f2 }; // Read-Only1 Input States for P31 - P0
    enum { REG_INB  = 0x1f3 }; // Read-Only1 Input States for P63- P323
    enum { REG_OUTA = 0x1f4 }; // Read/Write Output States for P31 - P0
    enum { REG_OUTB = 0x1f5 }; // Read/Write Output States for P63 <96> P323
    enum { REG_DIRA = 0x1f6 }; // Read/Write Direction States for P31 - P0
    enum { REG_DIRB = 0x1f7 }; // Read/Write Direction States for P63 - P323
    enum { REG_CTRA = 0x1f8 }; // Read/Write Counter A Control
    enum { REG_CTRB = 0x1f9 }; // Read/Write Counter B Control
    enum { REG_FRQA = 0x1fa }; // Read/Write Counter A Frequency
    enum { REG_FRQB = 0x1fb }; // Read/Write Counter B Frequency
    enum { REG_PHSA = 0x1fc }; // Read/Write2 Counter A Phase:
    enum { REG_PHSB = 0x1fd }; // Read/Write2 Counter B Phase
    enum { REG_VCFG = 0x1fe }; // Read/Write Video Configuration
    enum { REG_VSCL = 0x1ff }; // Read/Write Video Scale

    // src/dst fields
    //
    enum { SRC_START    = 0 };
    enum { SRC_MASK     = 0b111111111 << SRC_START };

    enum { DST_START    = 9 };
    enum { DST_MASK     = 0b111111111 << DST_START };

    // instruction modifiers
    //
    enum { ZCRI_START   = 22 };
    enum { ZCRI_MASK    = 0b1111 << ZCRI_START };
    enum { ZCRI_WZ      = 0b1000 << ZCRI_START };
    enum { ZCRI_WC      = 0b0100 << ZCRI_START };
    enum { ZCRI_WR      = 0b0010 << ZCRI_START };
    enum { ZCRI_IMMED   = 0b0001 << ZCRI_START };

    enum { COND_START   = 18 };
    enum { COND_MASK    = 0b1111 << COND_START };
    enum { COND_NOP     = 0b0000 << COND_START };     // nop if cond == 0

    // instruction values
    //
    enum { INS_START   = 26 };
    enum { INS_MASK    = 0b111111 << INS_START };
    enum { INS_HABYTE  = 0x00 << INS_START };     // hub access byte ... rdbyte or write byte depending upon r state
    enum { INS_HAWORD  = 0x01 << INS_START };
    enum { INS_HALONG  = 0x02 << INS_START };
    enum { INS_HUBOP   = 0x03 << INS_START };     // multiple meaning depending upon zcri and src bits
    enum { INS_MUL     = 0x04 << INS_START };     // broken unsigned multiply function
    enum { INS_MULS    = 0x05 << INS_START };     // broken signed multiply function
    enum { INS_ENC     = 0x06 << INS_START };     // broken encode function
    enum { INS_ONES    = 0x07 << INS_START };     // broken ones function
    enum { INS_ROR     = 0x08 << INS_START };     // rotate right function
    enum { INS_ROL     = 0x09 << INS_START };     // rotate left function
    enum { INS_SHR     = 0x0a << INS_START };     // shift right function
    enum { INS_SHL     = 0x0b << INS_START };     // shift left function
    enum { INS_RCR     = 0x0c << INS_START };     // rotate carry right function
    enum { INS_RCL     = 0x0d << INS_START };     // rotate carry left function
    enum { INS_SAR     = 0x0e << INS_START };     // arithmetic shift right function
    enum { INS_REV     = 0x0f << INS_START };     // reverse 32 function
    enum { INS_MINS    = 0x10 << INS_START };     // set D to S if signed D < S function
    enum { INS_MAXS    = 0x11 << INS_START };     // set D to S if signed D >= S function
    enum { INS_MIN     = 0x12 << INS_START };     // set D to S if unsigned D < S function
    enum { INS_MAX     = 0x13 << INS_START };     // set D to S if unsigned D >= S function
    enum { INS_MOVS    = 0x14 << INS_START };     // mov S to reg S
    enum { INS_MOVD    = 0x15 << INS_START };     // mov S to reg D
    enum { INS_MOVI    = 0x16 << INS_START };     // mov S to reg I
    enum { INS_JMP     = 0x17 << INS_START };     // insert PC+1 to D and set PC to S
    enum { INS_AND     = 0x18 << INS_START };     // the "and" instruction, use effects for test
    enum { INS_ANDN    = 0x19 << INS_START };     // the "andn" instruction, use effects for test
    enum { INS_OR      = 0x1a << INS_START };     // the "or" instruction,
    enum { INS_XOR     = 0x1b << INS_START };     // the "xor" instruction,
    enum { INS_MUXC    = 0x1c << INS_START };     // copy C to bits in D using S as mask
    enum { INS_MUXNC   = 0x1d << INS_START };     // copy !C to bits in D using S as mask
    enum { INS_MUXZ    = 0x1e << INS_START };     // copy Z to bits in D using S as mask
    enum { INS_MUXNZ   = 0x1f << INS_START };     // copy !Z to bits in D using S as mask
    enum { INS_ADD     = 0x20 << INS_START };     // add S to D save result in D
    enum { INS_SUB     = 0x21 << INS_START };     // sub S from D save result in D
    enum { INS_ADDABS  = 0x22 << INS_START };     // add abs of S to D save result in D
    enum { INS_SUBABS  = 0x23 << INS_START };     // sub abs of S from D save result in D
    enum { INS_SUMC    = 0x24 << INS_START };     // sum either -S if C or S if not C into D
    enum { INS_SUMNC   = 0x25 << INS_START };     // sum either S if C or -S if not C into D
    enum { INS_SUMZ    = 0x26 << INS_START };     // sum either -S if Z or S if not Z into D
    enum { INS_SUMNZ   = 0x27 << INS_START };     // sum either S if Z or -S if not Z into D
    enum { INS_MOV     = 0x28 << INS_START };     // mov S to D
    enum { INS_NEG     = 0x29 << INS_START };     // set D to -S
    enum { INS_ABS     = 0x2a << INS_START };     // set D to absolute S
    enum { INS_ABSNEG  = 0x2b << INS_START };     // set D to -absolute S
    enum { INS_NEGC    = 0x2c << INS_START };     // set D to either -S if C or S if not C
    enum { INS_NEGNC   = 0x2d << INS_START };     // set D to either S if C or -S if not C
    enum { INS_NEGZ    = 0x2e << INS_START };     // set D to either -S if Z or S if not Z
    enum { INS_NEGNZ   = 0x2f << INS_START };     // set D to either S if Z or -S if not Z
    enum { INS_CMPS    = 0x30 << INS_START };     // cmp signed D to S
    enum { INS_CMPSX   = 0x31 << INS_START };     // cmp sign extended D to S+C
    enum { INS_ADDX    = 0x32 << INS_START };     // add extended S+C to D
    enum { INS_SUBX    = 0x33 << INS_START };     // subtract extended S+C from D, use R=0 for cmpx
    enum { INS_ADDS    = 0x34 << INS_START };     // add signed S to D
    enum { INS_SUBS    = 0x35 << INS_START };     // subtract signed S from D, use R=0 for cmpx
    enum { INS_ADDSX   = 0x36 << INS_START };     // add signed extended S+C to D
    enum { INS_SUBSX   = 0x37 << INS_START };     // subtract signed extended S+C from D
    enum { INS_CMPSUB  = 0x38 << INS_START };     // subtract S from D if D >= S
    enum { INS_DJNZ    = 0x39 << INS_START };     // dec D, jmp if not zero to S. no jump = 8 clocks
    enum { INS_TJNZ    = 0x3a << INS_START };     // test D, jmp if not zero to S. no jump = 8 clocks
    enum { INS_TJZ     = 0x3b << INS_START };     // test D, jmp if zero to S. no jump = 8 clocks
    enum { INS_WAITPEQ = 0x3c << INS_START };     // wait for pins equal (INA & S) == D
    enum { INS_WAITPNE = 0x3d << INS_START };     // wait for pins not equal (INA & S) != D
    enum { INS_WAITCNT = 0x3e << INS_START };     // wait for CNT = D, then add S to D
    enum { INS_WAITVID = 0x3f << INS_START };     // wait for video peripheral to get D and S

private:
    int     lastcog;
    bool    cogstarted[COGCOUNT];
    bool    cogrun[COGCOUNT];
    bool    locks[COGCOUNT];
    bool    lockalloc[COGCOUNT];
    uint32  pindir[PINCOUNT];
    uint32  pinout[PINCOUNT];
    uint32  ina[COGCOUNT];
    uint32  inb[COGCOUNT];
    uint32  outa[COGCOUNT];
    uint32  outb[COGCOUNT];
    uint32  dira[COGCOUNT];
    uint32  dirb[COGCOUNT];
    uint32  frqa[COGCOUNT];
    uint32  frqb[COGCOUNT];
    uint32  phsa[COGCOUNT];
    uint32  phsb[COGCOUNT];
    uint32  vcfg[COGCOUNT];
    uint32  vscl[COGCOUNT];
    int     cog_zflag[COGCOUNT];
    int     cog_cflag[COGCOUNT];
    int32   waitflag[COGCOUNT];

    int     pc0[COGCOUNT];
    int     pc1[COGCOUNT];

    uchar   memory[MEMSIZE];
    uint32  cogmem[COGCOUNT][COGSIZE];

    bool    running;
    uint    loopcount;
    bool    debugCycleMode;

    int     interpAddr;
    bool    printinst;
    bool    breakOnCoginit;

    CogInfo coginfo[COGCOUNT];
    QHash<QString,int> brkHash;
    bool    passBreak;
    int     brkKeyNum;

    QTime   time;
    QLabel  syscntLabel;

    TraceEditor traceEdit;
    bool        traceon;

    QGridLayout layout;
    QList<PinType*> pins;
    QGroupBox   dropBox;

signals:

    void    addCogInstructions(int cog, QList<uint> code);
    void    clearBreakpoints(int cog);
    void    showBreakpoint(int cog, int line);
    void    clearCogMem(int cog);
    void    setCogPC(int cog, int pc0);
    void    showCog(int cog, CogInfo *coginfo);
    void    showCogTab(int cog);
    void    clearHubMem();
    void    showHubMem(uchar *memptr, int size);
    void    pokeHubMem(int addr, int value, int size);
    void    stopRunner();

    void    sendPinState(uint bits, uint time, uint period);

public slots:

    void    execute();
    void    setBreakpoint(int r, int cog);
    void    clearBreakpoint(int r, int cog);
    void    showBreakpoints();
    void    newCoreBreak(bool enable);

};

#endif // P8X32A_H

